package com.ternnetwork.oa.service.impl.work;

import com.ternnetwork.commons.model.http.BaseResponse;

import com.ternnetwork.commons.utils.DateUtils;
import com.ternnetwork.core.accesscontroll.entity.User;
import com.ternnetwork.core.accesscontroll.service.UserService;
import com.ternnetwork.core.accesscontroll.vo.SimpleUser;
import com.ternnetwork.oa.dto.work.EventDto;
import com.ternnetwork.oa.entity.work.Event;
import com.ternnetwork.oa.entity.work.EventUser;
import com.ternnetwork.oa.entity.work.EventUserId;
import com.ternnetwork.oa.enums.messages.EventMessages;
import com.ternnetwork.oa.repository.work.EventRepository;
import com.ternnetwork.oa.repository.work.EventUserRepository;
import com.ternnetwork.oa.service.work.EventEmailService;
import com.ternnetwork.oa.service.work.EventService;
import com.ternnetwork.oa.vo.work.EventVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;


@Service
public class EventServiceImpl implements EventService {

    @Autowired
    private EventRepository eventRepository;
    @Autowired
    private EventUserRepository eventUserRepository;
    @Autowired
    private UserService userService;
    @Autowired
    private EventEmailService eventEmailService;



    @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor=Exception.class)
    public BaseResponse add(EventDto dto,String username){

        BaseResponse  baseResponse=checkProperties(dto);

        if(baseResponse.getStatus().intValue()==0) {
            return baseResponse;
        }

        Event event=new Event();

        BeanUtils.copyProperties(dto,event);

        User loginUser=userService.findByName(username);

        event.setCreatedById(loginUser.getId());

        setEventTime(dto, event);

        eventRepository.saveAndFlush(event);

        dto.getUserIds().add(loginUser.getId());

        for(Long userId:dto.getUserIds()){

            User user=(User)userService.getOne(userId).getData();

            saveEventUser(event, user);

            eventEmailService.sendEmailForCreate(dto.getTitle(),dto.getStart(),dto.getEnd(),user.getName(),user.getEmail());

        }

        baseResponse.setSccucess();

        return baseResponse;
    }


    @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor=Exception.class)
    @Override
    public BaseResponse update(EventDto dto, String username) {

        Optional<Event> eventOptional=eventRepository.findById(dto.getId());

        User loginUser=userService.findByName(username);

        BaseResponse baseResponse=new BaseResponse();

        if(eventOptional.equals(Optional.empty())){

            baseResponse.setFailedForObjectNotExist();

            return baseResponse;
        }

        Event event=eventOptional.get();

        if(event.getCreatedById()!=loginUser.getId()){

            baseResponse.setFailedForObjectNotBelongsToLoginUser();

            return baseResponse;
        }

        BeanUtils.copyProperties(dto,event);

        if(isCurrentTimeLessThanEndTime(dto)==Boolean.TRUE){

            baseResponse.setFailed(EventMessages.FAILED_MESSAGE_EVENT_END_TIME_CAN_NOT_LESS_THAN_CURRENT_TIME.getName());

            return baseResponse;
        }

        setEventTime(dto, event);

        eventRepository.saveAndFlush(event);

        dto.getUserIds().add(loginUser.getId());

        List<EventUser> eventUserList=eventUserRepository.findAllByEventIdAndExcludeUserIdList(event.getId(),dto.getUserIds());

        for(EventUser eventUser:eventUserList){
            eventEmailService.sendEmailForDelete(event.getTitle(),eventUser.getUser().getName(),eventUser.getUser().getEmail());
        }

        eventUserRepository.deleteAllByEventIdAndExcludeUserIdList(event.getId(),dto.getUserIds());

        for(Long userId:dto.getUserIds()){

            User user=(User)userService.getOne(userId).getData();

            saveEventUser(event, user);

        }

        eventUserList=eventUserRepository.findAll(event.getId());

        for(EventUser eventUser:eventUserList){
            eventEmailService.sendEmailForUpdate(event.getTitle(),eventUser.getUser().getName(),eventUser.getUser().getEmail());
        }

        baseResponse.setSccucess();

        return baseResponse;
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor=Exception.class)
    public BaseResponse updateStartEnd(EventDto dto,String username) {

        Optional<Event> eventOptional=eventRepository.findById(dto.getId());

        User loginUser=userService.findByName(username);

        BaseResponse baseResponse=new BaseResponse();

        if(eventOptional.equals(Optional.empty())){
            baseResponse.setFailedForObjectNotExist();
            return baseResponse;
        }

        Event event=eventOptional.get();

        if(event.getCreatedById()!=loginUser.getId()){
            baseResponse.setFailedForObjectNotBelongsToLoginUser();
            return baseResponse;
        }

        if(isCurrentTimeLessThanEndTime(dto)==Boolean.TRUE){

            baseResponse.setFailed(EventMessages.FAILED_MESSAGE_EVENT_END_TIME_CAN_NOT_LESS_THAN_CURRENT_TIME.getName());

            return baseResponse;
        }
        setEventTime(dto, event);

        eventRepository.saveAndFlush(event);

        List<EventUser> eventUserList=eventUserRepository.findAll(event.getId());

        for(EventUser eventUser:eventUserList){
            eventEmailService.sendEmailForUpdate(event.getTitle(),eventUser.getUser().getName(),eventUser.getUser().getEmail());
        }

        baseResponse.setSccucess();

        return baseResponse;
    }

    private void setEventTime(EventDto dto, Event event) {

        event.setStart(DateUtils.parseDate(dto.getStart(), DateUtils.FORMAT_DATE_TIME_DEFAULT));

        event.setEnd(DateUtils.parseDate(dto.getEnd(), DateUtils.FORMAT_DATE_TIME_DEFAULT));
    }

    private Boolean isCurrentTimeLessThanEndTime(EventDto dto){

        Date currentDate=new Date();

        Date end= DateUtils.parseDate(dto.getEnd(), DateUtils.FORMAT_DATE_TIME_DEFAULT);

        return end.getTime()<=currentDate.getTime();
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor=Exception.class)
    @Override
    public BaseResponse deleteById(Long id,String username) {

        Optional<Event> eventOptional=eventRepository.findById(id);

        BaseResponse baseResponse=new BaseResponse();

        if(eventOptional.equals(Optional.empty())){

            baseResponse.setFailedForObjectNotExist();

            return baseResponse;
        }

        Event event=eventOptional.get();

        User loginUser=userService.findByName(username);

        if(event.getCreatedById()!=loginUser.getId()){

            baseResponse.setFailedForObjectNotBelongsToLoginUser();

            return baseResponse;
        }
        Date currentDate=new Date();

        if(event.getEnd().getTime()<=currentDate.getTime()){

            baseResponse.setFailed(EventMessages.FAILED_MESSAGE_EVENT_END_TIME_CAN_NOT_LESS_THAN_CURRENT_TIME.getName());

            return baseResponse;
        }

        List<EventUser> eventUserList=eventUserRepository.findAll(event.getId());

        for(EventUser eventUser:eventUserList){
            eventEmailService.sendEmailForDelete(event.getTitle(),eventUser.getUser().getName(),eventUser.getUser().getEmail());
        }

        eventUserRepository.deleteByEventId(event.getId());

        eventRepository.delete(event);

        baseResponse.setSccucess();

        return baseResponse;
    }

    public BaseResponse findAll(String start,String end,String username){

        User loginUser=userService.findByName(username);

        Long loginUserId=loginUser.getId();

        return findAll(start, end,loginUserId);
    }

    public BaseResponse findAll(String start, String end, Long userId) {

        List<EventUser> eventUserList= eventUserRepository.findAll(userId,DateUtils.parseDate(start,DateUtils.FORMAT_DATE_TIME_DEFAULT),DateUtils.parseDate(end,DateUtils.FORMAT_DATE_TIME_DEFAULT));

        List<EventVo> eventVoList=new ArrayList<>();

        for(EventUser eventUser:eventUserList){

            Event event=eventUser.getEvent();

            EventVo eventVo = getEventVo(userId, event);

            eventVoList.add(eventVo);
        }

        BaseResponse  baseResponse=new BaseResponse();

        baseResponse.setSccucess(eventVoList);

        return baseResponse;
    }

    public BaseResponse findAll(String start, String end) {

        List<Event> eventList= eventRepository.findAll(DateUtils.parseDate(start,DateUtils.FORMAT_DATE_TIME_DEFAULT),DateUtils.parseDate(end,DateUtils.FORMAT_DATE_TIME_DEFAULT));

        List<EventVo> eventVoList=new ArrayList<>();

        for(Event event:eventList){

            EventVo eventVo = getEventVo(0L, event);

            eventVoList.add(eventVo);
        }

        BaseResponse  baseResponse=new BaseResponse();

        baseResponse.setSccucess(eventVoList);

        return baseResponse;
    }

    private EventVo getEventVo(Long userId, Event event) {

        EventVo eventVo=new EventVo();

        BeanUtils.copyProperties(event,eventVo);

        eventVo.setStart(DateUtils.format(event.getStart(),DateUtils.FORMAT_DATE_TIME_DEFAULT));

        eventVo.setEnd(DateUtils.format(event.getEnd(),DateUtils.FORMAT_DATE_TIME_DEFAULT));

        Date currentDate=new Date();

        if(event.getCreatedById().longValue()==userId&&currentDate.getTime()<event.getEnd().getTime()){

            eventVo.setEditAble(Boolean.TRUE);

        }

        setSimpleUserList(eventVo, event.getId(), userId);

        return eventVo;
    }

    private void setSimpleUserList(EventVo eventVo,Long eventId,Long loginUserId){

        List<EventUser> eventUserList= eventUserRepository.findAll(eventId);

        for(EventUser eventUser:eventUserList) {

            User user=eventUser.getUser();
            //we don't need to show creator as participant
            if(user.getId()!=loginUserId){
                SimpleUser simpleUser=new SimpleUser();
                BeanUtils.copyProperties(user,simpleUser);
                eventVo.getSimpleUserList().add(simpleUser);
            }
        }
    }

    private void saveEventUser(Event event, User user) {

        EventUserId eventUserId = getEventUserId(event.getId(), user.getId());

        EventUser eventUser=new EventUser();

        eventUser.setId(eventUserId);

        eventUser.setUser(user);

        eventUser.setEvent(event);

        Optional<EventUser> old= eventUserRepository.findById(eventUserId);

        if(old.equals(Optional.empty())){

            eventUserRepository.saveAndFlush(eventUser);

        }

    }

    private EventUserId getEventUserId(Long eventId, Long userId) {

        EventUserId eventUserId=new EventUserId();

        eventUserId.setUserId(userId);

        eventUserId.setEventId(eventId);

        return eventUserId;
    }


    private BaseResponse checkProperties (EventDto t) {

        BaseResponse  baseResponse=new BaseResponse();

        if(StringUtils.isEmpty(t.getTitle())) {
            baseResponse.setFailed(EventMessages.FAILED_MESSAGE_TITLE_CAN_NOT_BE_EMPTY.getName());
            return baseResponse;
        }

        if(StringUtils.isEmpty(t.getStart())) {
            baseResponse.setFailed(EventMessages.FAILED_MESSAGE_START_CAN_NOT_BE_EMPTY.getName());
            return baseResponse;
        }
        baseResponse.setSccucess();
        return baseResponse;
    }

}
